/**
 *******************************************************************************
 * @FileName  : user_main.c
 * @Author    : GaoQiu
 * @CreateDate: 2020-05-18
 * @Copyright : Copyright(C) GaoQiu
 *              All Rights Reserved.
 *******************************************************************************
 *
 * The information contained herein is confidential and proprietary property of
 * GaoQiu and is available under the terms of Commercial License Agreement
 * between GaoQiu and the licensee in separate contract or the terms described
 * here-in.
 *
 * This heading MUST NOT be removed from this file.
 *
 * Licensees are granted free, non-transferable use of the information in this
 * file under Mutual Non-Disclosure Agreement. NO WARRENTY of ANY KIND is provided.
 *
 *******************************************************************************
 */
#include "user_main.h"
#include "utils/soft_uart.h"
#include "utils/mdc.h"
#include "utils/pool_buf.h"
#include "utils/msg.h"
#include "utils/stimer.h"
#include "utils/math.h"
#include "utils/heap.h"
#include "utils/byte_stream.h"
#include "utils/lg_mem.h"

#include "stack/controller/ll_api.h"
#include "stack/controller/bb_api.h"
#include "stack/hci_defs.h"

#include "stack/host/dm_api.h"
#include "stack/host/l2cap_api.h"
#include "stack/host/att_api.h"
#include "stack/host/att_uuid.h"
#include "stack/host/smp_api.h"
#include "stack/host/hci_api.h"

#include "bsp/bsp.h"
#include "component/usb/usb_api.h"
#include "component/kv/kv_api.h"
#include "svc_disc.h"

#include "profile/svc_handle.h"

/*!< Heap Size */
#define APP_HEAP_MEM_SIZE      (21 * 1024)
static uint32_t heap[APP_HEAP_MEM_SIZE/sizeof(uint32_t)] = {0};

/*!< LED blink period Unit:ms */
#define APP_LED_EN             1
#define APP_LED_BLINK_PERIOD   1000

/*!< Enable MAC Address filter */
#define APP_MAC_FILTER_EN      0
#define APP_FILTER_MAC         0x66, 0x66, 0x66, 0x66, 0x66, 0x66
#define APP_FILTER_LEN         6

/*!< Enable White-list  */
#define APP_WL_EN              0
#define APP_WL_NUM             1

/*!< SDP enable/disable */
#define APP_SDP_EN             1

/*! Connection parameter configuration */
#define APP_MAX_MST_NUM        1
#define APP_MAX_SLV_NUM        0
#define APP_MAX_CONN_NUM       (APP_MAX_MST_NUM + APP_MAX_SLV_NUM)

#define APP_SCAN_TYPE          LL_ACTIVE_SCAN
#define APP_SCAN_INTR          SCAN_INTR_50_MS
#define APP_SCAN_WIN           SCAN_INTR_50_MS
#define APP_SCAN_ADDR_TYPE     LL_ADDR_PUBLIC
#define APP_SCAN_FILTER        (APP_WL_EN ? LL_SCAN_FILTER_WL_BIT : LL_SCAN_FILTER_NONE)

#define APP_MST_CONN_INTR      CONN_INTR_15_MS
#define APP_MST_TIMEOUT        400

/*! Link layer config. */
const LlCfg_t llCfg = {
	versNr      : LL_VER_BT_CORE_SPEC, /*!< BLE Core version.           */
	compId      : LL_COMPANY_ID,       /*!< Company ID.                 */
	subVersNr   : LL_SUB_VERSION_NUM,  /*!< Implementation version.     */

	wlNum       : APP_WL_NUM,          /*!< Whitelist size.             */
	rlNum       : 1,                   /*!< Resolve list size.          */
	connCreateTo: 4,                   /*!< Create connection timeout. 0: disable timeout timer, other: timeout time, unit:s */

	numAdvSet         : 0,
	extAdvDataLen     : 0,
	extScanRspDataLen : 0,
	advMargin         : 0,             /*!< unit:ms */

	maxMst      : APP_MAX_MST_NUM,     /*!< Max number of master connection. */
	maxSlv      : APP_MAX_SLV_NUM,     /*!< Max number of slave connection. */
	maxAclLen   : 251,                 /*!< Length of ACL data.         */
	numRxBufs   : 16,                  /*!< Number of Rx buffer.        */
	numTxBufs   : 16,                  /*!< Number of Tx buffer.        */
	localSCA    : 300,                 /*!< Local sleep clock accuracy. */

	mstMargin   : 3,                   /*!< Master connection margin. Unit: 0.625ms. */
	numMoreDat  : 32,                  /*!< the max number of more data. Range: >=1. */

	authPyldTimeoutEn: false,
};

typedef struct{
	uint16_t        connHandle;
	uint8_t         role;
	uint8_t         inUse;

	MdcTimer_t      phyUpdTimer;
	MdcTimer_t      dleUpdTimer;
	MdcTimer_t      connUpdTimer;
	MdcTimer_t      sdpTimer;
}AppCCB_t;

/*! Application main control block. */
typedef struct{
	AppCCB_t        ccb[APP_MAX_CONN_NUM];
	MdcTimer_t      timer;
	MdcHandlerId_t  handlerId;

	uint8_t         startConn;
	uint8_t         numSlvConn;
	uint8_t         numMstConn;
}AppCb_t;

AppCb_t appCb;

AppCCB_t *APP_AllocCcb(uint16_t connHandle, uint8_t role)
{
	AppCCB_t *pCcb = appCb.ccb;
	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->inUse == false){
			pCcb->inUse = true;
			pCcb->connHandle = connHandle;
			pCcb->role       = role;

			/* Start PHY Update Timer*/
			MDC_TimerConfigEx(&pCcb->phyUpdTimer, appCb.handlerId, APP_EVT_PHY_UPD, 0, connHandle);
			MDC_TimerStartMs(&pCcb->phyUpdTimer, 500);

			/* Start DLE Update Timer*/
			MDC_TimerConfigEx(&pCcb->dleUpdTimer, appCb.handlerId, APP_EVT_DLE_UPD, 0, connHandle);
			MDC_TimerStartMs(&pCcb->dleUpdTimer, 1000);

			/* Start Connection Update Timer*/
			MDC_TimerConfigEx(&pCcb->connUpdTimer, appCb.handlerId, APP_EVT_CONN_UPD, 0, connHandle);
			MDC_TimerStartMs(&pCcb->connUpdTimer, 4000);

			/* Start SDP Timer*/
			MDC_TimerConfigEx(&pCcb->sdpTimer, appCb.handlerId, APP_EVT_SDP_START, 0, connHandle);
			MDC_TimerStartMs(&pCcb->sdpTimer, 1000);

			return pCcb;
		}
	}
	return NULL;
}

void APP_ReleaseCcbByHandle(uint16_t connHandle)
{
	AppCCB_t *pCcb = appCb.ccb;
	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->connHandle == connHandle && pCcb->inUse){
			pCcb->inUse = false;

			MDC_TimerStop(&pCcb->phyUpdTimer);
			MDC_TimerStop(&pCcb->dleUpdTimer);
			MDC_TimerStop(&pCcb->connUpdTimer);
			MDC_TimerStop(&pCcb->sdpTimer);
			return;
		}
	}
}

AppCCB_t *APP_GetCCB(uint16_t connHandle)
{
	AppCCB_t *pCcb = appCb.ccb;
	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->connHandle == connHandle && pCcb->inUse){
			return pCcb;
		}
	}
	return NULL;
}

void APP_MsgHandler(MdcEventMask_t event, MdcMsgHdr_t *pMsg)
{
	(void)event;

	if(pMsg)
	{
		switch(pMsg->event)
		{
		case APP_EVT_LED_BLINK:
			BSP_LedToggle(LED_GREEN_PIN);
			MDC_TimerStartMs(&appCb.timer, APP_LED_BLINK_PERIOD);
			break;

		case APP_EVT_DISCONN:
			break;

		case APP_EVT_PHY_UPD:
		{
			uint16_t connHandle = pMsg->param;
			UNUSED(connHandle);

			//LL_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_1M_BIT, LL_PHYS_LE_1M_BIT, LL_PHY_OPTIONS_NONE);
			//LL_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_2M_BIT, LL_PHYS_LE_2M_BIT, LL_PHY_OPTIONS_NONE);
			//LL_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_CODED_BIT, LL_PHYS_LE_CODED_BIT, LL_PHY_OPTIONS_S8_PREFERRED);
			//LL_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_CODED_BIT, LL_PHYS_LE_CODED_BIT, LL_PHY_OPTIONS_S2_PREFERRED);
			//LL_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_1M_BIT|LL_PHYS_LE_2M_BIT, LL_PHYS_LE_1M_BIT|LL_PHYS_LE_2M_BIT, LL_PHY_OPTIONS_NONE);
			//APP_TRACK_WRN("Start PHY Upd...\r\n");
			break;
		}
		case APP_EVT_DLE_UPD:
		{
			uint16_t connHandle = pMsg->param;
			UNUSED(connHandle);

			//HCI_LE_SetDataLen(connHandle, llCfg.maxAclLen, LL_MAX_TX_RX_TIME_1M(llCfg.maxAclLen));
			//APP_TRACK_WRN("Start DLE Upd...\r\n");
			break;
		}
		case APP_EVT_CONN_UPD:
		{
			uint16_t connHandle = pMsg->param;
			UNUSED(connHandle);

			LlConnSpec_t newConnSpec;
			newConnSpec.connIntervalMax = CONN_INTR_11P25_MS;//CONN_INTR_18P75_MS;
			newConnSpec.connIntervalMin = CONN_INTR_11P25_MS;//CONN_INTR_18P75_MS;
			newConnSpec.connLatency = 0;
			newConnSpec.supTimeout = 400;
			newConnSpec.maxCeLen = 0;
			newConnSpec.minCeLen = 0;

			//HCI_LE_ConnUpd(connHandle, &newConnSpec);
			//APP_TRACK_WRN("Start Connection Upd...\r\n");
			break;
		}
		case APP_EVT_SDP_START:
		{
		#if APP_SDP_EN
			APP_TRACK_INFO("====Start SDP====\r\n");
			uint16_t connHandle = pMsg->param;
			UNUSED(connHandle);
			extern void SDP_Start(uint16_t connHandle);
			SDP_Start(connHandle);
		#endif
			break;
		}
		default:
			break;
		}
	}
}

void APP_DisconnectAll(void)
{
	AppCCB_t *pCcb = appCb.ccb;
	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->inUse){
			DM_Disconnect(pCcb->connHandle, HCI_ERR_REMOTE_USER_TERM_CONN);
		}
	}
}

/******************************************************************************/
/* Key Handle                                                                 */
/******************************************************************************/
#ifdef TLSR8258_DK48
static uint8_t  keyType     = KEY_TYPE_NONE;
static uint8_t  keyPressed  = false;
static uint32_t keyScanTick = 0;

static uint8_t A_dataSendStart = 0;

void App_KeyInit(void)
{
	KB_Init();
}

void App_KeyChangeHandler(void)
{
	uint8_t  key0 = kbCb.keyCode[0];
	uint16_t consumerKey = 0;
	uint8_t  keyboard[8]={0};

	keyPressed = true;

	switch(kbCb.cnt)
	{
	case 0://key release
		appCb.startConn = false;

		keyPressed = false;
		if(keyType & KEY_TYPE_CONSUME){

		}
		if(keyType & KEY_TYPE_KEYBOARD){

		}
		keyType = KEY_TYPE_NONE;
		break;

	case 1:// one key pressed
		if(key0 == CR_VOL_UP){
			consumerKey = MKEY_VOL_UP;
			A_dataSendStart = true;
		}
		else if(key0 == CR_VOL_DN){
			consumerKey = MKEY_VOL_DN;
			A_dataSendStart = false;
		}
		else if(key0 == BTN_PAIR){
			keyboard[2] = 0x1e;

			/* Trigger connection. */
			appCb.startConn = true;
			//APP_TRACK_INFO("Start Connection...\r\n");
		}
		else if(key0 == BTN_UNPAIR){
			keyboard[2] = 0x1f;

			/* Disconnect all connections. */
			APP_DisconnectAll();
		}

		if(key0 == CR_VOL_UP || key0 == CR_VOL_DN){
			keyType |= KEY_TYPE_CONSUME;
		}
		else if(key0 == BTN_PAIR || key0 == BTN_UNPAIR){
			keyType |= KEY_TYPE_KEYBOARD;
		}
		else{
			keyType = KEY_TYPE_NONE;
		}

		if(keyType & KEY_TYPE_CONSUME){

		}
		else if(keyType & KEY_TYPE_KEYBOARD){

		}
		break;

	case 2://two key pressed
		break;

	default:
		break;
	}
}

void App_KeyTaskStart(void)
{
	if(!SysTimer_IsTimeExpired(keyScanTick, 20 * 1000)){  //keyScan interval: 20mS
		return;
	}

	keyScanTick = SysTimer_GetCurTick();

	bool_t keyChanged = KB_Scan();

	if(keyChanged){
		App_KeyChangeHandler();
	}
}
#else
static uint8_t  keyPressed  = false;
static uint8_t  keyValid = false;
void App_KeyInit(void)
{
	Btn_Init();
}

void App_KeyChangeHandler(void)
{
	BtnData_t *pBtnData = Btn_GetData();

	uint16_t consumerKey = 0;
	keyPressed = true;

	switch(pBtnData->cnt)
	{
	case 0:
		if(keyValid){
			keyValid = false;
		}
		appCb.startConn = false;
		break;

	case 1:
		if(pBtnData->code[0] == BTN_UNPAIR){
			consumerKey = MKEY_VOL_DN;
			/* Trigger connection. */
			appCb.startConn = true;
			//APP_TRACK_INFO("Start Connection...\r\n");
		}
		else if(pBtnData->code[0] == BTN_PAIR){
			consumerKey = MKEY_VOL_UP;
			/* Disconnect all connections. */
			APP_DisconnectAll();
		}
		if(consumerKey){
			keyValid = true;
		}
		break;

	case 2:
		break;

	default:
		break;
	}
}

void App_KeyTaskStart(void)
{
	static uint32_t keyScanTick = 0;
	if(!SysTimer_IsTimeExpired(keyScanTick, 20 * 1000)){  //keyScan interval: 20mS
		return;
	}
	keyScanTick = SysTimer_GetCurTick();

	bool_t keyChanged = Btn_Scan();
	if(keyChanged)
	{
		App_KeyChangeHandler();
	}
}
#endif

/******************************************************************************/
/* BLE Call-Back Handle                                                       */
/******************************************************************************/
#if APP_WL_EN
bdAddr_t addrTbl[] = {
	{0x66, 0x66, 0x66, 0x66, 0x00, 0x02},
};
#endif

uint32_t A_dataSendTimer = 0;
uint32_t A_createConnStart = 0;
uint16_t A_connHandle = 0xffff;
uint32_t A_connCnt = 0;
uint8_t remoteAddr[] = {0x66, 0x66, 0x66, 0x66, 0x00, 0x00};
LlInitParam_t initParam =
{
	APP_SCAN_INTR,       /*!< scan interval */
	APP_SCAN_WIN,        /*!< scan window size */
	LL_INIT_FILTER_PEER, /*!< filter policy */
	LL_ADDR_PUBLIC,      /*!< local address type */
	LL_ADDR_PUBLIC,      /*!< peer address type */
	&remoteAddr[0],      /*!< local address */
};

LlConnSpec_t connSpec =
{
	APP_MST_CONN_INTR,  /*!< Min connection interval */
	APP_MST_CONN_INTR,  /*!< Max connection interval */
	0,                  /*!< latency */
	APP_MST_TIMEOUT,    /*!< timeout */
	3,                  /*!< min connection event(CE), unit:0.625ms */
	6,                  /*!< max connection event(CE), unit:0.625ms */
};

LlConnSpec_t newConnSpec =
{
	APP_MST_CONN_INTR,  /*!< Min connection interval */
	APP_MST_CONN_INTR,  /*!< Max connection interval */
	0,                  /*!< latency */
	APP_MST_TIMEOUT,    /*!< timeout */
	3,                  /*!< min connection event(CE), unit:0.625ms */
	6,                  /*!< max connection event(CE), unit:0.625ms */
};

void DM_EvtHandler(DmEvt_t *pEvt)
{
	switch(pEvt->hdr.event)
	{
	case DM_ADV_REPORT_IND:
	{
		HciLeAdvReportEvt_t *p = (HciLeAdvReportEvt_t *)pEvt;

		//APP_TRACK_INFO("adv report:");
		//APP_TRACK_DAT((uint8_t*)&p->addr, 6);
		//APP_TRACK_INFO("adv rssi: %d\r\n", (int8_t)p->rssi);

		if(p->eventType >= HCI_ADV_DISC_UNDIRECT || p->rssi < -56){
			return;
		}
		//APP_TRACK_INFO("adv rssi: %d\r\n", (int8_t)p->rssi);

	#if APP_MAC_FILTER_EN
		/* Address Filter */
		uint8_t filterMac[] = {APP_FILTER_MAC};
		if(memcmp((uint8_t*)&p->addr, filterMac, APP_FILTER_LEN)){
			return;
		}
	#endif

		if(appCb.numMstConn >= llCfg.maxMst){
			return;
		}else{
			//appCb.startConn = true;
		}

		if(appCb.startConn)
		{
			memcpy(remoteAddr, (uint8_t *)&p->addr, BD_ADDR_LEN);
			initParam.peerAddrType = p->addrType & 0x01;
			appCb.startConn = false;
			//APP_TRACK_INFO("Start Connection...\r\n");
			LL_LE_CreateConn(&initParam, &connSpec);
		}
		break;
	}
	case DM_CONN_OPEN_IND:
	{
		if(pEvt->leConnCmpl.status){//Create connection cancel
			if(appCb.numMstConn < APP_MAX_MST_NUM){
				DM_LE_SetScanEnable(ENABLE, DISABLE);
			}
			APP_TRACK_WRN("Create connection timeout, Restart scan...\r\n");
			return;
		}

		APP_TRACK_INFO("****** Connection Cmpl ******\r\n\t"
				       "-connHandle:%d\r\n\t"
					   "-role:%s\r\n\t"
					   "-addr_type:%d\r\n\t"
				       "-peer_addr: ",
					   pEvt->leConnCmpl.handle,
					   pEvt->leConnCmpl.role == DM_ROLE_MASTER ? "Master": "Slave",
					   pEvt->leConnCmpl.peerAddrType);
		TRACK_DATA((uint8_t*)&pEvt->leConnCmpl.peerAddr, 6);

		BSP_LedOn(LED_RED_PIN);

		BSP_LedOff(LED_BLUE_PIN);
		BSP_LedOff(LED_WHITE_PIN);

		APP_AllocCcb(pEvt->leConnCmpl.handle, pEvt->leConnCmpl.role);

		A_createConnStart = SysTimer_GetCurTick()|1;
		A_connHandle = pEvt->leConnCmpl.handle;
		A_rx_pkt_cnt = 0;
		A_tx_pkt_cnt = 0;

		A_connCnt++;
		if(++appCb.numMstConn < pLlConfig->maxMst){
			DM_LE_SetScanEnable(ENABLE, DISABLE);
		}

		DM_ReadRemoteVerInfo(A_connHandle);
		break;
	}
	case DM_CONN_CLOSE_IND:
		BSP_LedOff(LED_RED_PIN);

		if(pEvt->disconnectCmpl.reason == HCI_ERR_CONN_TIMEOUT)
		{
			BSP_LedOn(LED_BLUE_PIN);
		}
		else if(pEvt->disconnectCmpl.reason == HCI_ERR_CONN_TERM_MIC_FAILURE){
			BSP_LedOn(LED_WHITE_PIN);
		}

		A_dataSendTimer = 0;
		A_connHandle = 0xFFFF;//invalid connection handle.

		A_connCnt--;
		if(--appCb.numMstConn < pLlConfig->maxMst){
			DM_LE_SetScanEnable(ENABLE, DISABLE);
		}
		APP_ReleaseCcbByHandle(pEvt->disconnectCmpl.handle);
		APP_TRACK_INFO("Disconnection Cmpl:\r\n\t connHandle: 0x%02x\r\n\t reason:0x%02x\r\n ",
				        pEvt->disconnectCmpl.handle, pEvt->disconnectCmpl.reason);

	#if APP_SDP_EN
		SDP_Delete(pEvt->disconnectCmpl.handle);
	#endif
		break;

	case DM_CONN_READ_REMOTE_VER_IND:
	{
		HciReadRemoteVerInfoCmplEvt_t *pCmpl = (HciReadRemoteVerInfoCmplEvt_t *)pEvt;
		DM_LE_ReadRemoteFeat(pCmpl->handle);
		APP_TRACK_INFO("Read Remote Version Complete...\r\n");
		break;
	}
	case DM_CONN_UPD_IND:
		APP_TRACK_INFO("Conn Upd Cmpl:\r\n\t-intvl:%d us\r\n\t-latency:%d\r\n\t-to:%d ms\r\n",
				       pEvt->leConnUpdateCmpl.connInterval *UNIT_1_25_MS,
					   pEvt->leConnUpdateCmpl.connLatency,
				       pEvt->leConnUpdateCmpl.supTimeout * 10);
		break;

	case DM_CONN_ENC_IND:
		APP_TRACK_INFO("Encryption Complete...\r\n");
		break;

	case DM_CONN_ENC_FAILED_IND:
		APP_TRACK_INFO("Encryption Failed...\r\n");
		break;

	case DM_CONN_DATA_LEN_CHANGE_IND:
	{
		HciLeDataLenChangeEvt_t *pCmpl = (HciLeDataLenChangeEvt_t *)pEvt;
		APP_TRACK_INFO("Data Length Exchange Complete...\r\n"
				"\t-tx_size:%d B(%u us)\r\n"
				"\t-rx_size:%d B(%u us)\r\n",
				pCmpl->maxTxOctets, pCmpl->maxTxTime,
				pCmpl->maxRxOctets, pCmpl->maxRxTime);
		(void)pCmpl;
		break;
	}
	case DM_CONN_PHY_UPD_IND:
	{
		HciLePhyUpdCmplEvt_t *pCmpl = (HciLePhyUpdCmplEvt_t *)pEvt;
		APP_TRACK_INFO("PHY update Cmpl:\r\n"
				"\t-PHY:%d\r\n", pCmpl->rxPhy);
		(void)pCmpl;
		break;
	}
	case DM_CONN_LE_READ_REMOTE_FEAT_IND:
	{
		APP_TRACK_INFO("LE Read Remote Feature Complete...\r\n");
		break;
	}
	case DM_L2C_CONN_PARAM_UPD_IND:
	{
		L2cConnParamUpdInd_t *pConnParamUpd = (L2cConnParamUpdInd_t *)pEvt;
        //if(llCfg.maxConn == 1)
		{
        	LlConnSpec_t connSpec;
        	connSpec.connIntervalMin = pConnParamUpd->intervalMin;
        	connSpec.connIntervalMax = pConnParamUpd->intervalMax;
        	connSpec.connLatency = pConnParamUpd->latency;
        	connSpec.supTimeout = pConnParamUpd->timeout;
        	connSpec.minCeLen = 0;
        	connSpec.maxCeLen = 0;
        	HCI_LE_ConnUpd(pConnParamUpd->hdr.param, &connSpec);
        }
		APP_TRACK_INFO("L2CAP connection param ind:");
        TRACK_DATA((uint8_t *)&pConnParamUpd->intervalMin, 8);
		break;
	}
	case DM_SMP_PIN_CODE_REQ:
	{
		SmpPinCodeReq_t *pCmpl = (SmpPinCodeReq_t *)pEvt;
		uint32_t pinCode = 123456;
		SMP_SetPinCode(pCmpl->connHandle, pinCode);
		APP_TRACK_INFO("Request Input PinCode: %u\r\n", pinCode);
		break;
	}
	case DM_SMP_PAIR_END_IND:
	{

	}

	default:
		break;
	}
}

#if APP_SDP_EN
uint16_t gwRxValueHandle = 0;
void SDP_CmplHandler(uint16_t connHandle, uint8_t reason)
{
	uint8_t UUID[] = {0x79, 0x41, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x02, 0x00, 0x40, 0x6E};
	Chr_t *pChr = SDP_FindCharByUUID(connHandle, UUID, 16);
	if(pChr){
		gwRxValueHandle = pChr->chr.valHandle;
		APP_TRACK_INFO("===> GW RX ATT Handle: 0x%04x(%d)\r\n", gwRxValueHandle,gwRxValueHandle);
	}
}

void SDP_Start(uint16_t connHandle)
{
	int res = SDP_Add(connHandle);
	if(res){
		APP_TRACK_ERR("SDP Add connection failed\r\n");
	}
	else{
		SDP_DiscAll(connHandle, SDP_CmplHandler);
	}
}
#endif

void ATT_EvtHandler(AttEvt_t *pEvt)
{
	AppCCB_t *pCcb = APP_GetCCB(pEvt->handle);
	if(pCcb == NULL){
		return;
	}

	switch(pEvt->hdr.event)
	{
	case ATT_MTU_UPDATE_IND:
		APP_TRACK_INFO("MTU Update Cmpl:\r\n"
				"\t-MTU:%d\r\n", pEvt->mtu);
		break;

	case ATTC_HANDLE_VALUE_NTF:
	{
	#if APP_SDP_EN
		uint16_t attHandle = 0;
		uint16_t keyCode = 0;
		uint16_t uuid = ATT_UUID_HID_REPORT;
		uint8_t *pPkt = pEvt->pValue;
		//uint32_t len  = pEvt->valueLen;

		pPkt++; //skip opcode
		BSTREAM_TO_UINT16(attHandle, pPkt);
		BSTREAM_TO_UINT16(keyCode, pPkt);

		A_rx_pkt_cnt++;
		APP_TRACK_INFO("RX Notify(AttHandle:%d,len:%d,rxCnt:%u): \r\n", attHandle, pEvt->valueLen-3, A_rx_pkt_cnt);
		APP_TRACK_DAT(pEvt->pValue + 3, MIN(pEvt->valueLen-3, 23));

		Chr_t *pChr = SDP_FindCharByHandle(pEvt->handle, attHandle);
		if(pChr && pChr->chr.uuid.len == ATT_16_UUID_LEN && pChr->chr.uuid.uuid_16 == uuid)
		{
			if(keyCode == 0x00E9 || keyCode == 0x0001){
				BSP_LedToggle(LED_BLUE_PIN);
			}
			else if(keyCode == 0x00EA || keyCode == 0x0002){
				BSP_LedToggle(LED_WHITE_PIN);
			}
		}
	#endif
		break;
	}

	case ATTC_HANDLE_VALUE_IND:
		ATTC_SendValueConfirm(pCcb->connHandle);
		break;

	default:
		break;
	}
}

void APP_Init(void)
{
	appCb.handlerId = MDC_AddMsgHandler(APP_MsgHandler);

#if APP_LED_EN
	appCb.timer.handlerId = appCb.handlerId;
	appCb.timer.msg.event = APP_EVT_LED_BLINK;
	MDC_TimerStartMs(&appCb.timer, APP_LED_BLINK_PERIOD);
#endif

	App_KeyInit();
}

void APP_Start(void)
{
	App_KeyTaskStart();
}

void Setup(void)
{
	/* memory initialization. */
	lg_mem_init();
	HeapInit((uint8_t *)&heap[0], APP_HEAP_MEM_SIZE);

	/* KV initialization. */
	kv_init(CFG_KV_FLASH_BASE_ADDR, CFG_KV_BLOCK_SIZE, CFG_KV_BLOCK_NUM);

	/* BSP initialization. */
	BSP_InitAllLed();

	/* Buffer pool alloc. */
	pool_desc_t pools[] = {
		{16,     8},
		{32,     8},
		{80,     16},
		{128,    4 },
		{280,    4 },
	};
	PoolBufInit(pools, COUNTOF(pools));

	/* MDC system initialization. */
	MDC_Init();

	/* MDC Timer initialization. */
	MDC_TimerInit();

	/* Set MAC address. */
	uint8_t bdAddr[6] = {0x66, 0x66, 0x66, 0x66, 0x00, 0x00};
	GetConfigParam(CFG_PARAM_MAC_ADDR, bdAddr, 6);
	LL_SetBdAddr(bdAddr, BD_ADDR_LEN);

	APP_TRACK_INFO("BD_ADDR:");
	APP_TRACK_DAT(bdAddr, BD_ADDR_LEN);

	/* LL initialization. */
	LL_Init(&llCfg);
	LL_MstInit();
	BB_SetTxPower(BB_POWER_P3p01_dBm);

	hostCfg.numMaxConn = APP_MAX_CONN_NUM;

	/* HCI initialization. */
	HCI_Init();
	HCI_SetMaxAclLen(247 + 4); /*!< Set L2CAP payload size= 104B ==> ATT MTU Size = 100. */

	/* DM initialization. */
	DM_Init();
	DM_AdvInit();
	DM_SecInit();
	DM_EvtRegister(DM_EvtHandler);
	DM_SecSetInitiateSecEnable(false);

	/* L2CAP initialization. */
	L2C_Init();
	L2C_MstInit();

	/* ATT initialization. */
	ATT_Init();
	ATTC_Init();
	ATT_EvtRegister(ATT_EvtHandler);

	/* SMP initialization. */
#if 0
	SMP_NonInit();
#else
	SMP_Init();
	SMP_InitiatorInit();
#endif

    /* Scan initialization. */
	DM_LE_SetScanParam(APP_SCAN_TYPE,          /*!< scan type.          */
			           APP_SCAN_INTR,          /*!< scan interval.      */
			           APP_SCAN_WIN,           /*!< scan window.        */
			           APP_SCAN_ADDR_TYPE,     /*!< local address type. */
			           APP_SCAN_FILTER);       /*!< scan filter policy. */
	DM_LE_SetScanEnable(ENABLE, DISABLE);

#if APP_WL_EN
	for(uint16_t i=0; i<APP_WL_NUM; i++){
		DM_LE_AddDeviceToWhiteList(DM_ADDR_PUBLIC, addrTbl[i]);
	}
#endif

#if APP_SDP_EN
	SDP_Init(APP_MAX_MST_NUM, 10, 64, 20);
#endif

	/* Application initialization. */
	APP_Init();
}

#if 0
uint8_t data1[] = {
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, //80
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, //80
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, //80
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
};
#endif

uint32_t A_loop=0;
void Loop(void)
{
	A_loop++;

	MDC_Start();

	APP_Start();

	if(A_createConnStart && SysTimer_IsTimeExpired(A_createConnStart, 6000*1000))
	{
		A_createConnStart = 0;
		A_dataSendTimer = SysTimer_GetCurTick()|1;
	}

#if 0
	if(A_dataSendTimer && SysTimer_IsTimeExpired(A_dataSendTimer, 20*1000))
	{
		A_dataSendTimer = SysTimer_GetCurTick()|1;

		AppCCB_t *pCcb = appCb.ccb;
		//for(int i=0; i<APP_MAX_CONN_NUM; i++, pCcb++)
		{
			if(pCcb->inUse && LL_GetFreeTxBufNum(pCcb->connHandle) > 2)
			{
				uint8_t *p = data1;
				if(ATTC_SendWriteCmd(pCcb->connHandle, GW_RX_VALUE_HDL, p, sizeof(data1)) == ATT_SUCCESS){
					p[0]++;
				}
			}
		}
	}
#endif
}

